﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace CacheNaga
{
	/// <summary>
	/// SocketPool封装了针对某一台memcached server的PooledSockets集合,并且包含获取和返回PooledSocket的方法
	/// </summary>
	internal class SocketPool
	{
		private static LogAdapter logger = LogAdapter.GetLogger(typeof(PooledSocket));

		/// <summary>
		/// 在memcached server挂了后,标识其为hang的状态的时间.
		/// 每进行一次hang的检查就加倍再次尝试的时间.当server
		/// 重新可以应用后再更改该值为1
		/// </summary>
		private int deadEndPointSecondsUntilRetry = 1;
		/// <summary>
		/// memcached server的最长死亡时间
		/// </summary>
		private const int maxDeadEndPointSecondsUntilRetry = 60 * 1; //1分钟
		/// <summary>
		/// Server pool
		/// </summary>
		private ServerPool owner;
		/// <summary>
		/// Memcached Server
		/// </summary>
		private IPEndPoint endPoint;
		/// <summary>
		/// 为该Server分配的PooledSocket
		/// </summary>
		private Queue<PooledSocket> queue;

		//Debug variables and properties
		private int newsockets = 0;
		private int failednewsockets = 0;
		private int reusedsockets = 0;
		private int deadsocketsinpool = 0;
		private int deadsocketsonreturn = 0;
		private int dirtysocketsonreturn = 0;
		private int acquired = 0;
		public int NewSockets { get { return newsockets; } }
		public int FailedNewSockets { get { return failednewsockets; } }
		public int ReusedSockets { get { return reusedsockets; } }
		public int DeadSocketsInPool { get { return deadsocketsinpool; } }
		public int DeadSocketsOnReturn { get { return deadsocketsonreturn; } }
		public int DirtySocketsOnReturn { get { return dirtysocketsonreturn; } }
		public int Acquired { get { return acquired; } }
		public int Poolsize { get { return queue.Count; } }

		#region Public variables and properties

		/// <summary>
		/// Memcached server host
		/// </summary>
		public readonly string Host;

		/// <summary>
		/// Memcached Server是否挂了
		/// </summary>
		private bool isEndPointDead = false;
		public bool IsEndPointDead { get { return isEndPointDead; } }

		/// <summary>
		/// 已经挂了的server的重试时间
		/// </summary>
		private DateTime deadEndPointRetryTime;
		public DateTime DeadEndPointRetryTime { get { return deadEndPointRetryTime; } }

		#endregion

		#region .ctor

		internal SocketPool(ServerPool owner, string host)
		{
			Host = host;
			this.owner = owner;
			endPoint = getEndPoint(host);
			queue = new Queue<PooledSocket>();
		}

		#endregion

		/// <summary>
		/// 将host转化为IPEndPoint类型
		/// </summary>
		private static IPEndPoint getEndPoint(string host)
		{
			//解析端口，如果没有指定，使用memcached默认的11211端口.
			int port = 11211;
			if (host.Contains(":"))
			{
				string[] split = host.Split(new char[] { ':' });
				if (!Int32.TryParse(split[1], out port))
				{
					throw new ArgumentException(string.Format("未能解析机器{0}上的端口{1}", host, port));
				}
				host = split[0];
			}

			//解析ip地址
			IPAddress address;
			if (!IPAddress.TryParse(host, out address))
			{
				//以hostname方式解析
				//慢!
				//所以最好不要使用指定hostname的方式
				try
				{
					address = Dns.GetHostEntry(host).AddressList[0];
				}
				catch (Exception e)
				{
					throw new ArgumentException("[CacheNaga-memcachedClient]未能解析host: " + host, e);
				}
			}

			return new IPEndPoint(address, port);
		}

		/// <summary>
		/// 从pool里面获取一个socket链接
		/// </summary>
		/// <remarks>
		/// 如果没有空闲的socket，将创建一个新返回。
		/// 如果在创建过程中出错，将认为该pool的endpoint为hang状态，标识并且对该方法的调用返回null直到再次被解析
		/// </remarks>
		internal PooledSocket Acquire()
		{
			//如果pool里有空闲的socket,返回第一个,否则创建一个新的
			Interlocked.Increment(ref acquired);
			lock (queue)
			{
				//如果有可用的PooledSocket
				while (queue.Count > 0)
				{
					//从队列中取出PooledSocket
					//如果socket可用,返回它
					PooledSocket socket = queue.Dequeue();
					if (socket != null && socket.IsAlive)
					{
						Interlocked.Increment(ref reusedsockets);//重用了socket计数
						return socket;
					}
					Interlocked.Increment(ref deadsocketsinpool);//死亡的socket计数
				}
			}

			Interlocked.Increment(ref newsockets);//创建新socket的计数
			//如果知道endpoint死了,检查是否当前时刻需要重试,否则返回null
			if (isEndPointDead)
			{
				if (DateTime.Now > deadEndPointRetryTime) //如果当前时间没有超过server死亡的重试链接时间
				{
					//重试
					isEndPointDead = false;
				}
				else
				{
					//在重试时间之前返回null
					return null;
				}
			}

			//尝试创建一个新的socket,如果失败,标记endpoint为死并且返回null
			try
			{
				PooledSocket socket = new PooledSocket(this, endPoint, owner.SendReceiveTimeout);
				//如果成功的话重置retry的时间
				deadEndPointSecondsUntilRetry = 1;
				return socket;
			}
			catch (Exception e)
			{
				Interlocked.Increment(ref failednewsockets);
				logger.Error("Error connecting to: " + endPoint.Address, e);
				//标识endpoint为死
				isEndPointDead = true;
				//设置下次检查服务器是否死亡的时间
				deadEndPointRetryTime = DateTime.Now.AddSeconds(deadEndPointSecondsUntilRetry);
				if (deadEndPointSecondsUntilRetry < maxDeadEndPointSecondsUntilRetry)
				{
					//如果重试机制失败,那么就
					deadEndPointSecondsUntilRetry = deadEndPointSecondsUntilRetry * 2;
				}
				return null;
			}
		}

		/// <summary>
		/// 从pool中返回一个socket
		/// </summary>
		/// <remarks>
		/// 如果socket死了,清之.如果超过MaxPoolSize设置,清之.如果小于MinPoolSize,返回pool中
		/// 如果超过了SocketRecycleAge的值,清之,否则重新放回pool中
		/// </remarks>
		internal void Return(PooledSocket socket)
		{
			//socket挂了,清之
			if (!socket.IsAlive)
			{
				Interlocked.Increment(ref deadsocketsonreturn);//增加死亡计数
				socket.Close();//一定要关闭掉socket
			}
			else
			{
				//清之
				if (socket.Reset())
				{
					Interlocked.Increment(ref dirtysocketsonreturn);
				}

				//检查pool的大小
				if (queue.Count >= owner.MaxPoolSize)
				{
					//如果pool满了,销毁socket
					socket.Close();
				}
				else if (queue.Count > owner.MinPoolSize && DateTime.Now - socket.Created > owner.SocketRecycleAge)
				{
					//如果socket没超出数量限制,但是socket存活时间超过了recycle age,清之
					socket.Close();
				}
				else
				{
					//把socket再重新放回pool
					lock (queue)
					{
						queue.Enqueue(socket);
					}
				}
			}
		}
	}
}
